using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Collections;
using System.Threading.Tasks;
using System.Threading;

namespace Mandelbrot_Florian
{
    public partial class MainForm : Form
    {

        private void draw_Click(object sender, EventArgs e)
        {
            //bmp = new Bitmap(imgMandelbrot.Width, imgMandelbrot.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            //System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, imgMandelbrot.Width, imgMandelbrot.Height),
            //                                                            System.Drawing.Imaging.ImageLockMode.ReadWrite,
            //                                                            bmp.PixelFormat);
            //IntPtr bmpPtr = bmpData.Scan0;

            //int bytes = imgMandelbrot.Width * imgMandelbrot.Height * 4;
            //byte[] argb = new byte[bytes];

            //double xStep = (P1.Re - P2.Re) / (imgMandelbrot.Width - 1);
            //double yStep = (P1.Im - P2.Im) / (imgMandelbrot.Height - 1);
            //double logEscapeRadius = Math.Log(escapeRadius);

            //Sci.Math.ComplexNumber z = P2;
            //progress.Maximum = imgMandelbrot.Width;

            //for (int x = 0; x < imgMandelbrot.Width; x++)
            //{
            //    z.Im = P1.Im;
            //    z.Re += xStep;
            //    for (int y = 0; y < imgMandelbrot.Height; y++)
            //    {
            //        z.Im -= yStep;
            //        Sci.Math.ComplexNumber C = z;
            //        int iteration = 0;
            //        while (z.Modulus < escapeRadius && iteration < maxIteration)
            //        {
            //            z = z * z + C;
            //            iteration++;
            //        }

            //        int colorIndex = 0;
            //        int index = (y * imgMandelbrot.Width + x) * 4;
            //        if (iteration < maxIteration)
            //        {
            //            z = z * z + C; iteration++;
            //            z = z * z + C; iteration++;
            //            double mu = iteration - (Math.Log(Math.Log(z.Modulus))) / logEscapeRadius;
            //            colorIndex = (int)(mu / maxIteration * 768);
            //            if (colorIndex >= 768) colorIndex = 0;
            //            if (colorIndex < 0) colorIndex = 0;
            //        }
            //        argb[index] = Colors[colorIndex].B;
            //        argb[index + 1] = Colors[colorIndex].G;
            //        argb[index + 2] = Colors[colorIndex].R;
            //        argb[index + 3] = 255;

            //        z = C;
            //    }
            //    progress.Value = x;
            //}
            //System.Runtime.InteropServices.Marshal.Copy(argb, 0, bmpPtr, argb.Length);
            //bmp.UnlockBits(bmpData);

            //imgMandelbrot.Invalidate();
            //progress.Value = 0;

            //oldP1 = P1;
            //oldP2 = P2;
            //oldSize = imgMandelbrot.Size;
            this.DrawMandel();
        }

        #region Attribut

        private double _Sx = -2.1;
        private double _Sy = -1.3;
        private double _Fx = 1;
        private double _Fy = 1.3;
        private Stopwatch _timer = new Stopwatch();
        private Rectangle _zoneSelection = new Rectangle();
        private bool _zooming = false;

        private int _maxIteration = 100;
        //private double escapeRadius = 2;
        private string[] _ColMaps;
        private Color[] _Colors;
        private Bitmap _bmp;
        private enum Methode { Normal, Thread, Task }
        private Methode _methode = Methode.Normal;

        private Thread _thread;

        #endregion

        public MainForm()
        {
            InitializeComponent();
        }
        private void MainForm_Load(object sender, EventArgs e)
        {
            _bmp = new Bitmap(imgMandelbrot.Width, imgMandelbrot.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            _ColMaps = System.IO.Directory.GetFiles(Application.StartupPath + @"\Colors", "*.ColorMap");
            foreach (string path in _ColMaps)
            {
                string[] splitPath = path.Split('\\');
                string name = splitPath[splitPath.Length - 1].Split('.')[0];
                cbColors.Items.Add(name);
            }
            cbColors.SelectedIndex = 0;
            initParam();
            UpdateLabels();
        }
        private void initParam()
        {
            _Sx = -2.1;
            _Sy = -1.3;
            _Fx = 1;
            _Fy = 1.3;
            _maxIteration = 100;
            //textBox1.Text = "2";
            //escapeRadius = 2;
        }
        private void UpdateLabels()
        {
            this.BeginInvoke(new Action(() =>
            {
                ntxtSx.Text = _Sx.ToString();
                ntxtSy.Text = _Sy.ToString();
                ntxtFx.Text = _Fx.ToString();
                ntxtFy.Text = _Fy.ToString();
                ntxtIteration.Value = _maxIteration;
                lbImgSize.Text = "(" + imgMandelbrot.Width + ", " + imgMandelbrot.Height + ")";
                lbSelection.Text = "F(" + _Fx + ", " + _Fy + ")\r\nS(" + _Sx + ", " + _Sy + ")";
            }));
        }
        private void Progressing(bool progress)
        {
            switch (_methode)
            {
                case Methode.Normal:
                    #region Sequence
                    if (progress)
                    {
                        _timer.Restart();
                        btnDraw.Enabled = false;
                        cbColors.Enabled = false;
                        progressBar.Maximum = imgMandelbrot.Width;
                        progressBar.Value = 0;
                        progressBar.Visible = true;
                    }
                    else
                    {
                        _timer.Stop();
                        lbTime.Text = String.Format("{0}.{1:000} s", _timer.Elapsed.Seconds, _timer.Elapsed.Milliseconds);
                        btnDraw.Enabled = true;
                        cbColors.Enabled = true;
                        progressBar.Visible = false;
                    }
                    #endregion
                    break;

                default:
                    #region Thread
                    if (progress)
                    {
                        _timer.Restart();
                        this.BeginInvoke(new Action(() =>
                        {
                            btnDraw.Enabled = false;
                            cbColors.Enabled = false;
                            progressBar.Maximum = imgMandelbrot.Width;
                            progressBar.Value = 0;
                            progressBar.Visible = true;
                        }));

                    }
                    else
                    {
                        _timer.Stop();
                        this.BeginInvoke(new Action(() =>
                        {
                            lbTime.Text = String.Format("{0}.{1:000} s", _timer.Elapsed.Seconds, _timer.Elapsed.Milliseconds);
                            btnDraw.Enabled = true;
                            cbColors.Enabled = true;
                            progressBar.Visible = false;
                        }));

                    }
                    #endregion
                    break;
            }

        }
        private void updateProgressBar()
        {
            switch (_methode)
            {
                case Methode.Normal:
                    progressBar.Value++;
                    break;

                default:
                    this.BeginInvoke(new Action(() =>
                    {
                        progressBar.Value++;
                    }));
                    break;
            }
            
        }
        private Color[] GetColors(string Path)
        {
            try
            {
                Color[] c = new Color[256];
                System.IO.StreamReader sr = new System.IO.StreamReader(Path);
                ArrayList lines = new ArrayList();
                string s = sr.ReadLine();
                while (s != null)
                {
                    lines.Add(s);
                    s = sr.ReadLine();
                }
                int i = 0;
                for (i = 0; i < Math.Min(256, lines.Count); i++)
                {
                    string curC = (string)lines[i];
                    Color temp = Color.FromArgb(int.Parse(curC.Split(' ')[0]), int.Parse(curC.Split(' ')[1]), int.Parse(curC.Split(' ')[2]));
                    c[i] = temp;
                }
                for (int j = i; j < 256; j++)
                {
                    c[j] = Color.White;
                }
                return c;
            }
            catch (Exception ex)
            {
                throw new Exception("Invalid ColorMap file.", ex);
            }
        }


        #region Zoom

        private void imgMandelbrot_MouseDown(object sender, MouseEventArgs e)
        {
            _zoneSelection = new Rectangle();
            _zoneSelection.Location = e.Location;
            _zooming = true;
        }
        private void imgMandelbrot_MouseMove(object sender, MouseEventArgs e)
        {
            if (_zooming)
            {
                int w = _zoneSelection.Location.X - e.X;
                int h = _zoneSelection.Location.Y - e.Y;

                _zoneSelection.Width = w;
                _zoneSelection.Height = h;
                imgMandelbrot.Invalidate();

                double sx, sy, fx, fy;
                zoom(_zoneSelection, out sx, out sy, out fx, out fy);
                lbSelection.Text = "F(" + fx + ", " + fy + ")\r\nS(" + sx + ", " + sy + ")";
            }

        }
        private void imgMandelbrot_MouseUp(object sender, MouseEventArgs e)
        {
            double sx, sy, fx, fy;
            zoom(_zoneSelection, out sx, out sy, out fx, out fy);
            _Sx = sx;
            _Sy = sy;
            _Fx = fx;
            _Fy = fy;

            _zooming = false;
            UpdateLabels();
            this.DrawMandel();
        }

        private void imgMandelbrot_Paint(object sender, PaintEventArgs e)
        {
            if (_zoneSelection != Rectangle.Empty && _zooming)
            {
                Pen pen = new Pen(Color.Red);
                e.Graphics.DrawRectangle(pen, _zoneSelection.X, _zoneSelection.Y, -_zoneSelection.Width, -_zoneSelection.Height);
            }
        }

        private void zoom(Rectangle selection, out double sx, out double sy, out double fx, out double fy)
        {
            if (selection.Width >= 0 || selection.Height >= 0)
            {
                initParam();
                sx = _Sx;
                sy = _Sy;
                fx = _Fx;
                fy = _Fy;
            }
            else
            {
                double intigralX = (_Fx - _Sx) / imgMandelbrot.Width;//Interval between each X (each pixel)
                double intigralY = (_Fy - _Sy) / imgMandelbrot.Height;//Interval between each Y (each pixel)
                //double ratioX = (_Fx - _Sx) / (imgMandelbrot.Width - 1);
                //double ratioY = (_Fy - _Sy) / (imgMandelbrot.Height - 1);

                sx = _Sx + (_zoneSelection.Location.X * intigralX);
                fy = _Fy - (_zoneSelection.Location.Y * intigralY);
                fx = sx + (-_zoneSelection.Width * intigralX);
                sy = fy - (-_zoneSelection.Height * intigralY);
            }
        }

        #endregion

        private void DrawMandel()
        {
            #region Local Attribut
            Bitmap b = new Bitmap(imgMandelbrot.Width, imgMandelbrot.Height);
            double x, y, x1, y1, xx, xmin, xmax, ymin, ymax = 0.0;
            int looper, s, z = 0;
            double intigralX, intigralY = 0.0;
            xmin = _Sx;
            ymin = _Sy;
            xmax = _Fx;
            ymax = _Fy;
            intigralX = (xmax - xmin) / imgMandelbrot.Width;//Interval between each X (each pixel)
            intigralY = (ymax - ymin) / imgMandelbrot.Height;//Interval between each Y (each pixel)
            x = xmin;
            #endregion

            Progressing(true);

            for (s = 1; s < imgMandelbrot.Width; s++)
            {
                y = ymin;
                for (z = 1; z < imgMandelbrot.Height; z++)
                {
                    x1 = 0;
                    y1 = 0;
                    looper = 0;
                    while (looper < _maxIteration && Math.Sqrt((x1 * x1) + (y1 * y1)) < 2)
                    {
                        looper++;
                        xx = (x1 * x1) - (y1 * y1) + x;
                        y1 = 2 * x1 * y1 + y;
                        x1 = xx;
                    }
                    double perc = looper / (double)_maxIteration;
                    int val = ((int)(perc * 255));
                    b.SetPixel(s, z, _Colors[val]);
                    y += intigralY;
                }
                x += intigralX;
                updateProgressBar();
            }

            Progressing(false);
            _bmp = b;
            imgMandelbrot.Image = _bmp;

        }
        private void DrawMandelByThread()
        {
            #region Local Attribut
            Bitmap b = new Bitmap(imgMandelbrot.Width, imgMandelbrot.Height);
            double x, y, x1, y1, xx, xmin, xmax, ymin, ymax = 0.0;
            int looper, s, z = 0;
            double intigralX, intigralY = 0.0;
            xmin = _Sx;
            ymin = _Sy;
            xmax = _Fx;
            ymax = _Fy;
            intigralX = (xmax - xmin) / imgMandelbrot.Width;//Interval between each X (each pixel)
            intigralY = (ymax - ymin) / imgMandelbrot.Height;//Interval between each Y (each pixel)
            x = xmin;
            #endregion

            Progressing(true);

            for (s = 1; s < imgMandelbrot.Width; s++)
            {
                y = ymin;
                for (z = 1; z < imgMandelbrot.Height; z++)
                {
                    x1 = 0;
                    y1 = 0;
                    looper = 0;
                    while (looper < _maxIteration && Math.Sqrt((x1 * x1) + (y1 * y1)) < 2)
                    {
                        looper++;
                        xx = (x1 * x1) - (y1 * y1) + x;
                        y1 = 2 * x1 * y1 + y;
                        x1 = xx;
                    }
                    double perc = looper / (double)_maxIteration;
                    int val = ((int)(perc * 255));
                    b.SetPixel(s, z, _Colors[val]);
                    y += intigralY;
                }
                x += intigralX;
                updateProgressBar();
            }

            Progressing(false);
            _bmp = b;
            imgMandelbrot.Image = _bmp;
            _thread.Abort();
        }
        private void DrawMandelByTask()
        {
            #region Local Attribut
            Bitmap b = new Bitmap(imgMandelbrot.Width, imgMandelbrot.Height);
            double x, y, x1, y1, xx, xmin, xmax, ymin, ymax = 0.0;
            int looper, z = 0;
            double intigralX, intigralY = 0.0;
            xmin = _Sx;
            ymin = _Sy;
            xmax = _Fx;
            ymax = _Fy;
            intigralX = (xmax - xmin) / imgMandelbrot.Width;//Interval between each X (each pixel)
            intigralY = (ymax - ymin) / imgMandelbrot.Height;//Interval between each Y (each pixel)
            x = xmin;
            #endregion

            Progressing(true);

            //// Use type parameter to make subtotal a long, not an int
            //Parallel.For<long>(0, nums.Length, () => 0, (j, loop, subtotal) =>
            //{
            //    subtotal += nums[j];
            //    return subtotal;
            //},
            //    (x) => Interlocked.Add(ref total, x)
            //);

            Parallel.For(1, imgMandelbrot.Width, s =>
            {
                y = ymin;
                for (z = 1; z < imgMandelbrot.Height; z++)
                {
                    x1 = 0;
                    y1 = 0;
                    looper = 0;
                    while (looper < _maxIteration && Math.Sqrt((x1 * x1) + (y1 * y1)) < 2)
                    {
                        looper++;
                        xx = (x1 * x1) - (y1 * y1) + x;
                        y1 = 2 * x1 * y1 + y;
                        x1 = xx;
                    }
                    double perc = looper / (double)_maxIteration;
                    int val = ((int)(perc * 255));
                    b.SetPixel(s, z, _Colors[val]);
                    y += intigralY;
                }
                x += intigralX;
                updateProgressBar();
            }); // Parallel.For

            Progressing(false);
            _bmp = b;
            imgMandelbrot.Image = _bmp;
        }


        private void btnDraw_Click(object sender, EventArgs e)
        {
            switch (_methode)
            {
                case Methode.Task:
                    this.DrawMandelByTask();
                    break;
                case Methode.Thread:
                    _thread = new Thread(new ThreadStart(this.DrawMandelByThread));
                    _thread.Start();
                    break;
                case Methode.Normal:
                    this.DrawMandel();
                    break;
                default:
                    break;
            }
            _zoneSelection = new Rectangle();
        }
        private void btnReset_Click(object sender, EventArgs e)
        {
            this.initParam();
            //this.WindowState = FormWindowState.Normal;
            //this.Size = this.MinimumSize;
            this.UpdateLabels();
            btnDraw.PerformClick();
        }
        private void btnSave_Click(object sender, EventArgs e)
        {
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
                _bmp.Save(saveFileDialog1.FileName);
        }


        #region Parameters Events

        private void ntxtFx_ValueChanged(object sender, EventArgs e)
        {
            _Fx = Double.Parse(ntxtFx.Text);
        }
        private void ntxtFy_ValueChanged(object sender, EventArgs e)
        {
            _Fy = Double.Parse(ntxtFy.Text);
        }
        private void ntxtSx_ValueChanged(object sender, EventArgs e)
        {
            _Sx = Convert.ToDouble(ntxtSx.Text);
        }
        private void ntxtSy_ValueChanged(object sender, EventArgs e)
        {
            _Sy = Convert.ToDouble(ntxtSy.Text);
        }
        private void ntxtIteration_ValueChanged(object sender, EventArgs e)
        {
            _maxIteration = (int)ntxtIteration.Value;
        }

        private void rbTheard_CheckedChanged(object sender, EventArgs e)
        {
            if (rbTheard.Checked)
                _methode = Methode.Thread;
        }
        private void rbTask_CheckedChanged(object sender, EventArgs e)
        {
            if (rbTask.Checked)
                _methode = Methode.Task;

        }
        private void rbNo_CheckedChanged(object sender, EventArgs e)
        {
            if (rbNo.Checked)
                _methode = Methode.Normal;
        }

        private void cbColors_SelectedIndexChanged(object sender, EventArgs e)
        {
            _Colors = GetColors(_ColMaps[cbColors.SelectedIndex]);
        }

        #endregion
    }
}